package com.itextpdf.local.ess.verify;

import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import com.itextpdf.text.pdf.parser.*;
import com.itextpdf.text.pdf.security.PdfPKCS7;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchProviderException;
import java.security.Security;
import java.security.cert.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

public class VerifyPdfUtil {

    public static VerifySignInfo verify(byte[] file) {
        List<byte[]> cert = new ArrayList<>();
        return verify(file,cert);
    }
    /**
     * 获取pdf签名图片信息
     * @return              提取结果
     */
    public static VerifySignInfo verify(byte[] file,List<byte[]> cert) {

        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

        VerifySignInfo pdfSignInfo = new VerifySignInfo();

        try {
            PdfReader reader = new PdfReader(file);
            Field rsaDataField = PdfPKCS7.class.getDeclaredField("RSAdata");
            rsaDataField.setAccessible(true);
            //文档总页数
            int numberOfPages = reader.getNumberOfPages();
            int xrefSize = reader.getXrefSize();
            // 获取acro字段
            AcroFields fields = reader.getAcroFields();
            // 获取每页的大小
//            Rectangle pageSize = reader.getPageSize(numberOfPages - 1);
            // pdf 作用域尺寸（全屏）
//            log.info("pdf rectangle: {},{},{},{}", pageSize.getLeft(), pageSize.getBottom(), pageSize.getRight(), pageSize.getTop());
            // 获取签名名称
            ArrayList<String> signatureNames = fields.getSignatureNames();
            //遍历签名
            for (String name : signatureNames) {
                // 指定提供者 获取签名的pkcs7数据
                PdfPKCS7 pkcs7 = fields.verifySignature(name,"BC");
                //从签名中取得证书
                X509Certificate certificate = pkcs7.getSigningCertificate();
                //创建返回结果中的一个签名
                VerifySignInfo.SignatureDetail info = new VerifySignInfo.SignatureDetail();

                String[] appearanceStates = fields.getAppearanceStates(name);

                //填写签名验证结果
                info.setDocumentVerify(pkcs7.verify());
                //填写改签名是否覆盖整个文档
                info.setCompleteVerify(fields.signatureCoversWholeDocument(name));
                //填写证书验证结果
                if (cert.size()>0){
                    for (byte[] bCert :cert){
                        boolean result1 = VerifyIssuer(certificate.getEncoded(),bCert);
                        if (result1) {
                            info.setCertVerify(true);
                            break;
                        }
                    }
                }
                // 表单域的位置
                List<AcroFields.FieldPosition> fieldPositions = fields.getFieldPositions(name);
                for (AcroFields.FieldPosition fieldPosition : fieldPositions) {
                    Rectangle position = fieldPosition.position;
//                    log.debug("field {} position: {}, {}, {}, {}, {}", name, fieldPosition.page,
//                            position.getLeft(), position.getBottom(), position.getRight(), position.getTop());
                    info.page = fieldPosition.page;
                    info.llx = position.getLeft();
                    info.lly = position.getBottom();
                    info.urx = position.getRight();
                    info.ury = position.getTop();
                }
                AcroFields.Item fieldItem = fields.getFieldItem(name);
                int size = fieldItem.size();
                for (int i = 0; i < size; i++) {
                    PdfDictionary value = fieldItem.getValue(i);
//                    Set<PdfName> keys = value.getKeys();
//                    for (PdfName key : keys) {
//                        String keyName = PdfName.decodeName(new String(key.getBytes()));
//                        if ("Rect".equalsIgnoreCase(keyName)) {
//                            log.info("签名域的坐标为：{}", value.get(key));
//                        }
//                    }
                    PdfObject pdfObject = value.get(PdfName.AP);
                    if (pdfObject != null) {
                        PdfDictionary dictionary = (PdfDictionary) pdfObject;
                        PdfIndirectReference ref = (PdfIndirectReference) dictionary.get(PdfName.N);
                        int number = ref.getNumber();
//                        log.debug("fieldName: {}, idx: {}", name, number);
                        // 根据ap 获取签章图片
                        PdfObject pdfObjectRelease = reader.getPdfObject(number);
                        if (pdfObjectRelease instanceof PdfStream) {
                            PdfStream s = (PdfStream) pdfObjectRelease;
                            PdfDictionary resources = s.getAsDict(PdfName.RESOURCES);
                            try {
                                final byte[][] imageByte = new byte[1][1];
                                final int[] height = {0};
                                final int[] width = {0};
                                PdfContentStreamProcessor processor = new PdfContentStreamProcessor(new RenderListener() {
                                    @Override
                                    public void renderImage(ImageRenderInfo renderInfo) {
                                        try {
                                            PdfImageObject image  = renderInfo.getImage();
                                            if (image == null) {
//                                                log.debug("Image {} could not be read", renderInfo.getRef().getNumber());
                                                return;
                                            }
                                            BufferedImage bufferedImage = image.getBufferedImage();
                                            if (bufferedImage != null) {
                                                height[0] = bufferedImage.getHeight();
                                                width[0] = bufferedImage.getWidth();
                                                ByteArrayOutputStream os = new ByteArrayOutputStream();
                                                ImageIO.write(bufferedImage, "png", os);
                                                imageByte[0] = os.toByteArray();
                                            }
//                                                PRStream maskStream = (PRStream) image.getDictionary().getAsStream(PdfName.SMASK);
//                                                if (maskStream != null) {
//                                                    PdfImageObject pdfImageObject = new PdfImageObject(maskStream);
//                                                    log.debug("mask image: {}", pdfImageObject.getFileType());
//                                                    BufferedImage mask = pdfImageObject.getBufferedImage();
//                                                    writeImage(mask);
//                                                }
                                        } catch (IOException e) {
                                            e.printStackTrace();
                                        }
                                    }
                                    @Override
                                    public void beginTextBlock() {}
                                    @Override
                                    public void renderText(TextRenderInfo renderInfo) {}
                                    @Override
                                    public void endTextBlock() {}
                                });
                                processor.processContent(ContentByteUtils.getContentBytesFromContentObject(s), resources);
                                info.fileBase64 =imageByte[0];
                                info.imageHeight = height[0];
                                info.imageWidth = width[0];
                            } catch (Exception ignore) {

                            }
                        }
                    }
                }
                // 签名信息
                info.name = name;
                info.time = pkcs7.getSignDate().getTime();
                info.notBefore = certificate.getNotBefore();
                info.notAfter = certificate.getNotAfter();
                info.dnName = certificate.getSubjectDN().getName();
                info.serialNumber = certificate.getSerialNumber();
                info.pubKey = certificate.getPublicKey().getEncoded();
                info.pubKeyFormat = certificate.getPublicKey().getFormat();
                info.sigAlgName = certificate.getSigAlgName();
                info.userDnName = certificate.getIssuerDN().getName();
                info.reason = pkcs7.getReason();
                info.location = pkcs7.getLocation();
                info.res = pkcs7.verify();
                pdfSignInfo.getSignatureDetails().add(info);
            }
//            log.info(JSON.toJSONString(pdfSignInfo));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return pdfSignInfo;
    }



    /**
     * 获取pdf签名图片信息
     * @return              提取结果
     */
    public static VerifySignInfo verifyWithoutImg(byte[] file) {
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        VerifySignInfo pdfSignInfo = new VerifySignInfo();

        try {
            PdfReader reader = new PdfReader(file);
            Field rsaDataField = PdfPKCS7.class.getDeclaredField("RSAdata");
            rsaDataField.setAccessible(true);
            //文档总页数
            int numberOfPages = reader.getNumberOfPages();
            int xrefSize = reader.getXrefSize();
            // 获取acro字段
            AcroFields fields = reader.getAcroFields();
            // 获取每页的大小
//            Rectangle pageSize = reader.getPageSize(numberOfPages - 1);
            // pdf 作用域尺寸（全屏）
//            log.info("pdf rectangle: {},{},{},{}", pageSize.getLeft(), pageSize.getBottom(), pageSize.getRight(), pageSize.getTop());
            // 获取签名名称
            ArrayList<String> signatureNames = fields.getSignatureNames();
            //遍历签名
            for (String name : signatureNames) {
                // 指定提供者 获取签名的pkcs7数据
                PdfPKCS7 pkcs7 = fields.verifySignature(name,"BC");
                //从签名中取得证书
                X509Certificate certificate = pkcs7.getSigningCertificate();
                //创建返回结果中的一个签名
                VerifySignInfo.SignatureDetail info = new VerifySignInfo.SignatureDetail();

                String[] appearanceStates = fields.getAppearanceStates(name);

                //填写签名验证结果
                info.setDocumentVerify(pkcs7.verify());
                //填写改签名是否覆盖整个文档
                info.setCompleteVerify(fields.signatureCoversWholeDocument(name));
                // 表单域的位置
                List<AcroFields.FieldPosition> fieldPositions = fields.getFieldPositions(name);
                for (AcroFields.FieldPosition fieldPosition : fieldPositions) {
                    Rectangle position = fieldPosition.position;
//                    log.debug("field {} position: {}, {}, {}, {}, {}", name, fieldPosition.page,
//                            position.getLeft(), position.getBottom(), position.getRight(), position.getTop());
                    info.page = fieldPosition.page;
                    info.llx = position.getLeft();
                    info.lly = position.getBottom();
                    info.urx = position.getRight();
                    info.ury = position.getTop();
                }
                AcroFields.Item fieldItem = fields.getFieldItem(name);
                // 签名信息
                info.name = name;
                info.time = pkcs7.getSignDate().getTime();
                info.notBefore = certificate.getNotBefore();
                info.notAfter = certificate.getNotAfter();
                info.dnName = certificate.getSubjectDN().getName();
                info.serialNumber = certificate.getSerialNumber();
                info.pubKey = certificate.getPublicKey().getEncoded();
                info.pubKeyFormat = certificate.getPublicKey().getFormat();
                info.sigAlgName = certificate.getSigAlgName();
                info.userDnName = certificate.getIssuerDN().getName();
                info.reason = pkcs7.getReason();
                info.location = pkcs7.getLocation();
                info.res = pkcs7.verify();
                pdfSignInfo.getSignatureDetails().add(info);
            }
//            log.info(JSON.toJSONString(pdfSignInfo));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return pdfSignInfo;
    }


    // 验证证书的颁发者
    // bUserCert	:	用户证书
    // bRootCert	:	颁发者证书
    public static boolean VerifyIssuer(byte[] bUser,byte[] bRoot)
    {
        Security.addProvider(new BouncyCastleProvider());
        CertificateFactory certificateFactory;
        try {
            certificateFactory = CertificateFactory.getInstance("X.509","BC");
            ByteArrayInputStream bInput = new ByteArrayInputStream(bRoot);
            X509Certificate trustedCert = (X509Certificate) certificateFactory.generateCertificate(bInput);
            bInput.close();

            bInput = new ByteArrayInputStream(bUser);
            X509Certificate userCert = (X509Certificate) certificateFactory.generateCertificate(bInput);
            bInput.close();

            HashSet<TrustAnchor> trustAnchors = new HashSet<TrustAnchor>();
            trustAnchors.add(new TrustAnchor(trustedCert, null));

            CertificateFactory factory = CertificateFactory.getInstance("X509","BC");
            List<X509Certificate> certList = new ArrayList<X509Certificate>();
            certList.add(userCert);
            CertPath certPath = factory.generateCertPath(certList);
            PKIXParameters parameters = new PKIXParameters(trustAnchors);
            parameters.setRevocationEnabled(false);
            CertPathValidator validator = CertPathValidator.getInstance("PKIX","BC");

            PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult) validator.validate(certPath, parameters);
            TrustAnchor trustAnchor = result.getTrustAnchor();
            if(trustAnchor == null) {
                return false;
            } else {
                return true;
            }
        } catch (CertificateException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchProviderException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (CertPathValidatorException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (GeneralSecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return false;
    }
}
